<!--
 * @Author: taosong 2289713738@qq.com
 * @Date: 2024-01-25 19:57:38
 * @LastEditors: taosong 2289713738@qq.com
 * @LastEditTime: 2024-03-25 15:42:35
 * @FilePath: \Song-ui\src\components\Tooltip\Tooltip.vue
 * @Description: 
 * 
 * Copyright (c) 2024 by ${git_name_email}, All Rights Reserved. 
-->
<script setup lang="ts">
import { ref, watch, reactive, onUnmounted, computed } from 'vue'
import { createPopper, type Instance } from '@popperjs/core'
import type { TooltipEmits, TooltipProps, TooltipInstance } from './type'
import useClickOutside from '@/hooks/useClickOutside'
import { debounce } from 'lodash-es'

const props = withDefaults(defineProps<TooltipProps>(), {
  placement: 'bottom',
  trigger: 'hover',
  transition: 'fade',
  openDelay: 0,
  closeDelay: 0
})

const emits = defineEmits<TooltipEmits>()

const isOpen = ref(false)

let events: Record<string, any> = reactive({})
let outerEvents: Record<string, any> = reactive({})

// popper实例
let popperInstance: Instance | null = null

// dom节点
const popperNode = ref<HTMLElement>()
const triggerNode = ref<HTMLElement>()
const popperContainerNode = ref<HTMLElement>()

// 配置项
const propperOptions = computed(() => {
  return {
    placement: props.placement,
    // 偏移量
    modifiers: [{
      name: 'offset',
      options: {
        offset: [0, 9]
      }
    }],
    ...props.propperOptions
  }
})



const open = () => {
  console.log('打开')
  isOpen.value = true
  emits('visible-change', true)
}

const close = () => {
  console.log('关闭')
  isOpen.value = false
  emits('visible-change', false)
}

const openDebounce = debounce(open, props.openDelay)

const closeDebounce = debounce(close, props.closeDelay)

const openFinal = () => {
  closeDebounce.cancel()
  openDebounce()
}

const closeFinal = () => {
  openDebounce.cancel()
  closeDebounce()
}

const togglePopper = () => {
  if (isOpen.value) {
    closeFinal()
  } else {
    openFinal()
  }
  emits('visible-change', isOpen.value)
}

const attachEvents = () => {
  if (props.trigger === 'hover') {
    events['mouseenter'] = openFinal
    outerEvents['mouseleave'] = closeFinal
  } else if (props.trigger === 'click') {
    events['click'] = togglePopper
  }
}

// 不是手动触发执行
if (!props.manual) {
  attachEvents()
}

// 启用outside点击
useClickOutside(popperContainerNode, () => {
  if (props.trigger === 'click' && isOpen.value && !props.manual) {
    closeDebounce()
  }
})

// 监听触发方式改变
watch(
  () => props.trigger,
  (newTrigger, oldTrigger) => {
    if (newTrigger !== oldTrigger) {
      events = {}
      outerEvents = {}
      attachEvents()
    }
  }
)

watch(
  isOpen,
  (newVal) => {
    if (newVal) {
      // 创建prpper实例
      if (popperNode.value && triggerNode.value) {
        // 生成popper实例
        popperInstance = createPopper(triggerNode.value, popperNode.value, propperOptions.value)
      } else {
        // 销毁实例
        popperInstance?.destroy()
      }
    }
  },
  { flush: 'post' }
)

// 监听是否手动触发
watch(
  () => props.manual,
  (isManual) => {
    if (isManual) {
      // 手动触发
      events = {}
      outerEvents = {}
    } else {
      // 不是手动触发 挂载对应事件
      attachEvents()
    }
  }
)

// 在组件销毁时清除实例
onUnmounted(() => popperInstance?.destroy())

// 定义导出方法
defineExpose<TooltipInstance>({
  show: openFinal,
  hide: closeFinal
})
</script>

<template>
  <div ref="popperContainerNode" v-on="outerEvents" class="sg-tooltip">
    <div ref="triggerNode" class="sg-tooltip__trigger" v-on="events">
      <slot />
    </div>
    <Transition :name="transition">
      <div v-if="isOpen" ref="popperNode" class="sg-tooltip__popper">
        <slot name="content">{{ content }}</slot>
        <!-- 添加箭头 -->
        <div id="arrow" data-popper-arrow></div>
      </div>
    </Transition>
  </div>
</template>

<style lang="scss" scoped></style>
